/*Scala code license:

 Copyright 2009/10 Osman Pub

 Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 this file except in compliance with the License.

 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software distributed
 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 CONDITIONS OF ANY KIND, either express or implied. See the License for the
 specific language governing permissions and limitations under the License.

 */
/**
   Orginal java code credits:
 * @author Kevin J. Duling
 */
package demos

import common.GLDisplay
import java.awt.event._
import javax.media.opengl._
import javax.media.opengl.glu.GLU

object Lesson05 {
  def main(args: Array[String]) {
    val glDisplay = GLDisplay("Lesson 05: 3D Shapes")
    glDisplay addGLEventListener new Renderer
    glDisplay addKeyListener new KeyAdapter {
      override def keyReleased(e: KeyEvent) =
        e getKeyCode match { case _ => glDisplay stop }
    }
    glDisplay start
  }

  private class Renderer extends GLEventListener {
    /** Called by the drawable to initiate OpenGL rendering by the client.
     * After all GLEventListeners have been notified of a display event, the
     * drawable will swap its buffers if necessary.
     * @param gLDrawable The GLAutoDrawable object.
     */
    private var rquad, rtri = 0f

    def display(glDrawable: GLAutoDrawable) {
      val gl = glDrawable getGL;
      gl glClear GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT

      gl glLoadIdentity; gl glTranslatef(-1.5f, 0, -6)
      gl glRotatef(rtri, 0, 1, 0)

      gl glBegin(GL GL_TRIANGLES)		// Drawing Using Triangles
      gl glColor3f(1, 0, 0)			// Red
      gl glVertex3f(0, 1, 0)			// Top Of Triangle (Front)

      gl glColor3f(0, 1, 0)			// Green
      gl glVertex3f(-1, -1, 1)			// Left Of Triangle (Front)

      gl glColor3f(0, 0, 1)			// Blue
      gl glVertex3f(1, -1, 1)			// Right Of Triangle (Front)

      gl glColor3f(1, 0, 0)			// Red
      gl glVertex3f(0, 1, 0)			// Top Of Triangle (Right)

      gl glColor3f(0, 0, 1)			// Blue
      gl glVertex3f(1, -1, 1)			// Left Of Triangle (Right)

      gl glColor3f(0, 1, 0)			// Green
      gl glVertex3f(1, -1, -1)			// Right Of Triangle (Right)

      gl glColor3f(1, 0, 0)			// Red
      gl glVertex3f(0, 1, 0)			// Top Of Triangle (Back)

      gl glColor3f(0, 1, 0)			// Green
      gl glVertex3f(1, -1, -1)			// Left Of Triangle (Back)

      gl glColor3f(0, 0, 1)			// Blue
      gl glVertex3f(-1, -1, -1)			// Right Of Triangle (Back)

      gl glColor3f(1, 0, 0)			// Red
      gl glVertex3f(0, 1, 0)			// Top Of Triangle (Left)

      gl glColor3f(0, 0, 1)			// Blue
      gl glVertex3f(-1, -1, -1)			// Left Of Triangle (Left)

      gl glColor3f(0, 1, 0)			// Green
      gl glVertex3f(-1, -1, 1)			// Right Of Triangle (Left)

      gl glEnd;				// Finished Drawing The Triangle

      gl glLoadIdentity; gl glTranslatef(1.5f, 0, -6);
      gl glRotatef(rquad, 1, 0, 0)

      gl glBegin(GL GL_QUADS)           	// Draw A Quad

      gl glColor3f(0, 1, 0)			// Set The Color To Green
      gl glVertex3f(1, 1, -1)			// Top Right Of The Quad (Top)
      gl glVertex3f(-1, 1, -1)			// Top Left Of The Quad (Top)
      gl glVertex3f(-1, 1, 1)			// Bottom Left Of The Quad (Top)
      gl glVertex3f(1, 1, 1)			// Bottom Right Of The Quad (Top)

      gl glColor3f(1, 0.5f, 0)			// Set The Color To Orange
      gl glVertex3f(1, -1, 1)			// Top Right Of The Quad (Bottom)
      gl glVertex3f(-1, -1, 1)			// Top Left Of The Quad (Bottom)
      gl glVertex3f(-1, -1, -1)			// Bottom Left Of The Quad (Bottom)
      gl glVertex3f(1, -1, -1)			// Bottom Right Of The Quad (Bottom)

      gl glColor3f(1, 0, 0)			// Set The Color To Red
      gl glVertex3f(1, 1, 1)			// Top Right Of The Quad (Front)
      gl glVertex3f(-1, 1, 1)			// Top Left Of The Quad (Front)
      gl glVertex3f(-1, -1, 1)			// Bottom Left Of The Quad (Front)
      gl glVertex3f(1, -1, 1)			// Bottom Right Of The Quad (Front)

      gl glColor3f(1, 1, 0)			// Set The Color To Yellow
      gl glVertex3f(1, -1, -1)			// Bottom Left Of The Quad (Back)
      gl glVertex3f(-1, -1, -1)			// Bottom Right Of The Quad (Back)
      gl glVertex3f(-1, 1, -1)			// Top Right Of The Quad (Back)
      gl glVertex3f(1, 1, -1)			// Top Left Of The Quad (Back)

      gl glColor3f(0, 0, 1)			// Set The Color To Blue
      gl glVertex3f(-1, 1, 1)			// Top Right Of The Quad (Left)
      gl glVertex3f(-1, 1, -1)			// Top Left Of The Quad (Left)
      gl glVertex3f(-1, -1, -1)			// Bottom Left Of The Quad (Left)
      gl glVertex3f(-1, -1, 1)			// Bottom Right Of The Quad (Left)

      gl glColor3f(1, 0, 1)			// Set The Color To Violet
      gl glVertex3f(1, 1, -1)			// Top Right Of The Quad (Right)
      gl glVertex3f(1, 1, 1)			// Top Left Of The Quad (Right)
      gl glVertex3f(1, -1, 1)			// Bottom Left Of The Quad (Right)
      gl glVertex3f(1, -1, -1)			// Bottom Right Of The Quad (Right)

      gl glEnd;				// Done Drawing The Quad
      gl glFlush;

      rtri += 0.2f; rquad += 0.15f
    }


    /** Called when the display mode has been changed.  <B>!!
     * CURRENTLY UNIMPLEMENTED IN JOGL !!</B>
     * @param gLDrawable The GLAutoDrawable object.
     * @param modeChanged Indicates if the video mode has changed.
     * @param deviceChanged Indicates if the video device has changed.
     */
    def displayChanged(glDrawable: GLAutoDrawable, modeChanged: Boolean,
                       deviceChanged: Boolean) {}

    /** Called by the drawable immediately after the OpenGL context is
     * initialized for the first time. Can be used to perform one-time OpenGL
     * initialization such as setup of lights and display lists.
     * @param gLDrawable The GLAutoDrawable object.
     */
    def init(glDrawable: GLAutoDrawable) {
      val gl = glDrawable getGL;
      gl glShadeModel GL.GL_SMOOTH               // Enable Smooth Shading
      gl glClearColor(0, 0, 0, 0)     // Black Background
      gl glClearDepth 1                       // Depth Buffer Setup
      gl glEnable GL.GL_DEPTH_TEST 							// Enables Depth Testing
      gl glDepthFunc GL.GL_LEQUAL 						// The Type Of Depth Testing To Do
      gl glHint(GL GL_PERSPECTIVE_CORRECTION_HINT, GL GL_NICEST) 	
    }

    /** Called by the drawable during the first repaint after the component has
     * been resized. The client can update the viewport and view volume of the
     * window appropriately, for example by a call to
     * GL.glViewport(int, int, int, int) note that for convenience the component
     * has already called GL.glViewport(int, int, int, int)(x, y, width, height)
     * when this method is called, so the client may not have to do anything in
     * this method.
     * @param gLDrawable The GLAutoDrawable object.
     * @param x The X Coordinate of the viewport rectangle.
     * @param y The Y coordinate of the viewport rectanble.
     * @param width The new width of the window.
     * @param height The new height of the window.
     */
    def reshape(glDrawable: GLAutoDrawable, x: Int, y: Int, width: Int,
                height: Int) {
      require(height != 0)

      val gl = glDrawable getGL;
      gl glMatrixMode GL.GL_PROJECTION; gl glLoadIdentity;
      new GLU gluPerspective(45, width.asInstanceOf[Float] /
                             height.asInstanceOf[Float], 1, 20)
      gl glMatrixMode GL.GL_MODELVIEW; gl glLoadIdentity
    }
  }
}
